package com.tianji.promotion.service.impl;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.tianji.common.autoconfigure.redisson.annotations.Lock;
import com.tianji.common.domain.dto.PageDTO;
import com.tianji.common.exceptions.BizIllegalException;
import com.tianji.common.utils.BeanUtils;
import com.tianji.common.utils.ObjectUtils;
import com.tianji.common.utils.UserContext;
import com.tianji.promotion.domain.po.Coupon;
import com.tianji.promotion.domain.po.ExchangeCode;
import com.tianji.promotion.domain.po.UserCoupon;
import com.tianji.promotion.domain.query.UserCouponQuery;
import com.tianji.promotion.domain.vo.CouponVO;
import com.tianji.promotion.enums.ExchangeCodeStatus;
import com.tianji.promotion.mapper.CouponMapper;
import com.tianji.promotion.mapper.UserCouponMapper;
import com.tianji.promotion.service.IExchangeCodeService;
import com.tianji.promotion.service.IUserCouponService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tianji.promotion.utils.*;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.aop.framework.AopContext;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * <p>
 * 用户领取优惠券的记录，是真正使用的优惠券信息 服务实现类
 * </p>
 *
 * @author ffk
 * @since 2024-07-04
 */
@Slf4j
@RequiredArgsConstructor
@Service
public class UserCouponServiceImpl extends ServiceImpl<UserCouponMapper, UserCoupon> implements IUserCouponService {


    private  final CouponMapper couponMapper;
    private final IExchangeCodeService exchangeCodeService;
    private final StringRedisTemplate redisTemplate;
    private final  RedissonClient redissonClient;
    /**
     * 领取发放中的优惠券
     * @param id 优惠券id
     */
    @Override
    @Transactional
    public void getIssueCoupon(Long id) {
        Coupon coupon = couponMapper.selectById(id);
        if(ObjectUtils.isEmpty(coupon)){
            throw new BizIllegalException("优惠券不存在,怎么领？");
        }

        LocalDateTime now = LocalDateTime.now();
        if(now.isAfter(coupon.getIssueEndTime()) || now.isBefore(coupon.getIssueBeginTime())){
            throw new BizIllegalException("不在发放时间，怎么领？");
        }

        if(coupon.getIssueNum()>=coupon.getTotalNum()){
            throw new BizIllegalException("优惠券没库存了，怎么领？");
        }

        Long userId = UserContext.getUser();
        String key = "lock_coupon_key:"+userId;

        RLock lock = redissonClient.getLock(key);

        //RedisLock lock = new RedisLock(key,redisTemplate);
        boolean isLocked = lock.tryLock();
        if(!isLocked){
            throw new BizIllegalException("上锁失败");
        }

            IUserCouponService userCouponService = (IUserCouponService) AopContext.currentProxy();
            userCouponService.updateAndCreate(userId,coupon,null);
    }

    /**
     * 分页查询用户优惠券
     * @param userCouponQuery 优惠券状态
     */
    @Override
    public PageDTO<CouponVO> pageQueryUserCoupon(UserCouponQuery userCouponQuery) {

         Page<UserCoupon> page = this.lambdaQuery()
                .eq(UserCoupon::getUserId, UserContext.getUser())
                .eq(UserCoupon::getStatus, userCouponQuery.getStatus())
                .page(userCouponQuery.toMpPageDefaultSortByCreateTimeDesc());

         if(ObjectUtils.isEmpty(page)){
             log.warn("这个用户：{}没有优惠券", UserContext.getUser());
             return PageDTO.empty(0L,0L);
         }

        List<UserCoupon> records = page.getRecords();
        if(ObjectUtils.isEmpty(records)){
            return PageDTO.empty(page.getTotal(),page.getPages());
        }

        //我领的优惠券id们
        Set<Long> couponIds = records.stream().map(UserCoupon::getCouponId).collect(Collectors.toSet());
        //批量查出来
        List<Coupon> myCoupon = couponMapper.selectBatchIds(couponIds);

        List<CouponVO> voList=new ArrayList<>(myCoupon.size()+1);
        for (Coupon coupon : myCoupon) {
            CouponVO couponVO = BeanUtils.copyBean(coupon, CouponVO.class);
            voList.add(couponVO);
        }

        return  PageDTO.of(page,voList);
    }

    /**
     * 使用兑换码兑换优惠券
     * @param code 兑换码id
     */
    @Override
    @Lock(name="lock:coupon:#{T(com.tianji.common.utils.UserContext).getUser()}")
    public void exchangeCoupon(String code) {
        //解析
        long codeId = CodeUtil.parseCode(code);
        boolean b = exchangeCodeService.updateExchangeMark(codeId, true);
            if(b){//返回true，表示原来的位置上已经是1了，那么肯定换过了
                throw new BizIllegalException("兑换码已经兑换了宝");
            }

            try {
                //查询兑换码
            ExchangeCode codeMsg = exchangeCodeService.getById(codeId);
            if(ObjectUtils.isEmpty(codeMsg)){
                throw new BizIllegalException("兑换码不存在宝");
            }

            //过期时间
            LocalDateTime expiredTime = codeMsg.getExpiredTime();
            LocalDateTime now = LocalDateTime.now();
            if(now.isAfter(expiredTime)){
                throw new BizIllegalException("兑换码已经过期了宝");
            }

            Long couponId = codeMsg.getExchangeTargetId();
            Coupon coupon = couponMapper.selectById(couponId);
            if(ObjectUtils.isEmpty(coupon)){
                throw new BizIllegalException("优惠券不存在宝");
            }


            //锁加在事务之外
                 Long userId = UserContext.getUser();
                synchronized (userId.toString().intern()){
                    updateAndCreate(userId,coupon,codeId);
                }


        } catch (BizIllegalException e) {
            //redis回滚
            exchangeCodeService.updateExchangeMark(codeId, false);
            throw e;
        }

    }

    //更新优惠券表以及新增中间表
    // @MyLock(name= "lock:coupon" ,lockType = MyLockType.RE_ENTRANT_LOCK)
    @Transactional
    @Override
    @Lock(name= "lock:coupon:#{userId}")
    public void updateAndCreate(Long userId, Coupon coupon, Long code) {
        Integer myCount = this.lambdaQuery()
                .eq(UserCoupon::getCouponId, coupon.getId())
                .eq(UserCoupon::getUserId, userId)
                .count();
        if(myCount>= coupon.getUserLimit()){
            throw new BizIllegalException("已达兑换或者领取上限了宝");
        }

        //新增到中间表
        saveToUserCoupon(coupon, userId);

        //优惠券表更新
       int result =couponMapper.updateCouponById(coupon.getId());
       if(result==0){
           throw new BizIllegalException("优惠券库存不足，不能领取了宝");
       }

        //更新兑换码状态
        if (code != null) {
            exchangeCodeService.lambdaUpdate()
                    .set(ExchangeCode::getUserId, UserContext.getUser())
                    .set(ExchangeCode::getStatus, ExchangeCodeStatus.USED)
                    .eq(ExchangeCode::getId, code)
                    .update();
        }

    }

    //保存到中间表
    private void saveToUserCoupon(Coupon coupon,Long userId) {
        LocalDateTime now = LocalDateTime.now();
        LocalDateTime termBeginTime = coupon.getTermBeginTime();
        LocalDateTime termEndTime = coupon.getTermEndTime();
        if(ObjectUtils.isEmpty(termBeginTime) || termBeginTime==null){
            termBeginTime = now;
            termEndTime= now.plusDays(coupon.getTermDays());
        }

        //usercoupon表
        UserCoupon userCoupon = new UserCoupon();
        userCoupon.setTermBeginTime(termBeginTime);
        userCoupon.setTermEndTime(termEndTime);
        userCoupon.setCouponId(coupon.getId());
        userCoupon.setUserId(userId);
        boolean save = save(userCoupon);
        if(!save){
            throw new BizIllegalException("保存用户领取信息到中间表失败");
        }
    }
}
